SSH Login Notification
SSH login notifications add a real-time signal to your server security posture: when a successful SSH session opens, the server sends an alert containing the account name, source IP, hostname, and timestamp. This helps with rapid detection of unexpected access, auditing of legitimate admin activity, and faster incident response.
- Triggering an alert on successful SSH logins (session open)
- Two delivery methods: local mail (
mailutils) and external SMTP (msmtp) - Safe PAM integration that does not block logins if the alert fails
- This alerts on successful logins, not failed attempts. Pair with fail2ban and SSH hardening for brute-force protection.
- Email delivery from VPS providers may be restricted (blocked ports or poor deliverability). External SMTP is usually more reliable.
How It Works
On Linux, SSH sessions can be integrated with PAM (Pluggable Authentication Modules). PAM can run a script whenever a new SSH session is opened. The script gathers session details and sends an email.
Prerequisites
-
Root or
sudoaccess -
SSH server uses PAM (typical on Ubuntu/Debian)
-
A way to send mail:
- Local mail (
mailutils+ a local MTA), or - External SMTP (
msmtp, recommended for reliable delivery)
- Local mail (
Verify SSH uses PAM (server)
Check /etc/ssh/sshd_config:
UsePAM yes
Validate config and restart SSH if you changed it:
sudo sshd -t
sudo systemctl restart ssh
Choose an Email Delivery Method
- Local mail (mailutils)
- External SMTP (msmtp)
Local mail is simple but depends on your server’s ability to deliver email reliably. Some VPS providers block outbound SMTP or your messages may land in spam.
External SMTP is usually the best choice. Alerts are more likely to reach inboxes reliably, especially when your provider blocks outbound port 25 or when deliverability matters.
Method A: Local Mail Using mailutils
Step 1: Install mail tools
sudo apt update
sudo apt install mailutils -y
During installation, you may be prompted to configure an MTA (often Postfix). For basic usage you can pick:
- Local only (quick setup)
- Or configure appropriately if you need external delivery and your provider permits it
Step 2: Create the alert script
Create the script:
sudo nano /usr/local/bin/ssh_login_alert.sh
Paste this:
#!/bin/bash
# SSH login alert script (local mail)
# Runs from PAM. Keep it non-blocking and safe.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
RECIPIENT_EMAIL="your-email@example.com"
# PAM environment variables commonly available:
# PAM_USER, PAM_RHOST, PAM_SERVICE, PAM_TTY, PAM_TYPE
USER_NAME="${PAM_USER:-unknown}"
REMOTE_HOST="${PAM_RHOST:-unknown}"
HOST="$(hostname -f 2>/dev/null || hostname)"
DATE="$(date -u +"%Y-%m-%d %H:%M:%S UTC")"
# Only notify on session open events (avoid other PAM phases)
if [ "${PAM_TYPE:-}" != "open_session" ]; then
exit 0
fi
# If PAM_RHOST is empty (local), skip or keep based on preference
if [ -z "${PAM_RHOST:-}" ]; then
exit 0
fi
SUBJECT="SSH login: ${USER_NAME} from ${REMOTE_HOST} on ${HOST}"
BODY="$(cat <<EOF
SSH login notification
Host: ${HOST}
User: ${USER_NAME}
Remote IP: ${REMOTE_HOST}
Service: ${PAM_SERVICE:-sshd}
TTY: ${PAM_TTY:-unknown}
Time: ${DATE}
EOF
)"
# Send mail (do not fail login if mail fails)
echo "$BODY" | /usr/bin/mail -s "$SUBJECT" "$RECIPIENT_EMAIL" 2>/dev/null || true
exit 0
Update:
RECIPIENT_EMAILto your real email address
Make executable:
sudo chmod 700 /usr/local/bin/ssh_login_alert.sh
sudo chown root:root /usr/local/bin/ssh_login_alert.sh
Step 3: Enable PAM execution for SSH sessions
Edit PAM config:
sudo nano /etc/pam.d/sshd
Add at the bottom:
# SSH login notifications (do not block logins if script fails)
session optional pam_exec.so /usr/local/bin/ssh_login_alert.sh
Why optional matters:
- If the script fails, the SSH login should still succeed
Step 4: Test
- Open a second terminal session (keep one session connected for safety).
- Log out and log back in via SSH.
- Check your inbox.
Server-side sanity checks:
sudo tail -n 200 /var/log/auth.log
sudo journalctl -u ssh -n 200 --no-pager
Method B: External SMTP Using msmtp (Recommended)
Step 1: Install msmtp
sudo apt update
sudo apt install msmtp msmtp-mta -y
Step 2: Configure msmtp (global config)
Create or edit:
sudo nano /etc/msmtprc
Template (TLS via STARTTLS on port 587):
defaults
auth on
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /var/log/msmtp.log
account default
host smtp.your-provider.com
port 587
from server-alerts@your-domain.com
user your-smtp-username
password your-smtp-password
account default : default
Lock permissions (contains credentials):
sudo chown root:root /etc/msmtprc
sudo chmod 600 /etc/msmtprc
Create the log file (optional but useful):
sudo touch /var/log/msmtp.log
sudo chown root:root /var/log/msmtp.log
sudo chmod 600 /var/log/msmtp.log
Many SMTP providers require:
- The
fromaddress to be verified - TLS enabled
- An app password (for Gmail/Google Workspace) instead of your normal password
Step 3: Create/update the alert script to use msmtp
Edit:
sudo nano /usr/local/bin/ssh_login_alert.sh
Paste this:
#!/bin/bash
# SSH login alert script (external SMTP via msmtp)
# Runs from PAM. Keep it non-blocking and safe.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
RECIPIENT_EMAIL="your-personal-email@example.com"
FROM_EMAIL="server-alerts@your-domain.com"
USER_NAME="${PAM_USER:-unknown}"
REMOTE_HOST="${PAM_RHOST:-unknown}"
HOST="$(hostname -f 2>/dev/null || hostname)"
DATE="$(date -u +"%Y-%m-%d %H:%M:%S UTC")"
if [ "${PAM_TYPE:-}" != "open_session" ]; then
exit 0
fi
if [ -z "${PAM_RHOST:-}" ]; then
exit 0
fi
SUBJECT="SSH login: ${USER_NAME} from ${REMOTE_HOST} on ${HOST}"
BODY="$(cat <<EOF
SSH login notification
Host: ${HOST}
User: ${USER_NAME}
Remote IP: ${REMOTE_HOST}
Service: ${PAM_SERVICE:-sshd}
TTY: ${PAM_TTY:-unknown}
Time: ${DATE}
EOF
)"
# Construct RFC822 message and send via msmtp (-t reads headers)
(
echo "To: ${RECIPIENT_EMAIL}"
echo "From: ${FROM_EMAIL}"
echo "Subject: ${SUBJECT}"
echo
echo "${BODY}"
) | /usr/bin/msmtp --account=default -t 2>/dev/null || true
exit 0
Update:
RECIPIENT_EMAILandFROM_EMAILto match your SMTP configuration
Secure the script:
sudo chmod 700 /usr/local/bin/ssh_login_alert.sh
sudo chown root:root /usr/local/bin/ssh_login_alert.sh
Step 4: Enable PAM execution for SSH sessions
Edit:
sudo nano /etc/pam.d/sshd
Add at the bottom:
# SSH login notifications (do not block logins if script fails)
session optional pam_exec.so /usr/local/bin/ssh_login_alert.sh
Step 5: Test
- Log out and log back in via SSH.
- Confirm delivery in your inbox.
Check msmtp logs:
sudo tail -n 200 /var/log/msmtp.log
Optional Enhancements
Add Geo-IP Lookup (External Request)
This enriches notifications but depends on a third-party service and can fail if outbound HTTPS is blocked. Use only if acceptable for your environment.
Add this inside the script after REMOTE_HOST is known:
GEO_JSON="$(/usr/bin/curl -s --max-time 2 "https://ipinfo.io/${REMOTE_HOST}/json" || true)"
Then include GEO_JSON in the message body.
Notify Only for Specific Users
Example filter:
case "$USER_NAME" in
ubuntu|deploy|admin) ;;
*) exit 0 ;;
esac
Reduce Noise (Skip Non-interactive Sessions)
Some automation tools create SSH sessions without a full interactive TTY. You can skip if no TTY is present:
if [ -z "${PAM_TTY:-}" ] || [ "${PAM_TTY:-}" = "unknown" ]; then
exit 0
fi
Troubleshooting
| Symptom | Likely Cause | Checks / Fix |
|---|---|---|
| -- | - | |
| No emails received | Delivery blocked or misconfigured mail | For local mail: check postfix setup; for SMTP: verify /etc/msmtprc, provider credentials, TLS port |
| SSH works but no alert | PAM not triggering script | Confirm UsePAM yes; confirm line exists in /etc/pam.d/sshd |
| Script runs but email fails | Missing binaries or PATH issues | Use full paths (/usr/bin/msmtp, /usr/bin/mail); confirm packages installed |
| Login delays | Slow network calls in script | Avoid geo-IP or add timeouts (curl --max-time 2) |
| Provider rejects email | Bad From address or auth | Ensure verified sender; use app password; confirm TLS port and settings |
Useful logs:
sudo tail -n 200 /var/log/auth.log
sudo journalctl -u ssh -n 200 --no-pager
For external SMTP:
sudo tail -n 200 /var/log/msmtp.log
Rollback (Disable Notifications Safely)
- Remove or comment the line added to
/etc/pam.d/sshd:
# session optional pam_exec.so /usr/local/bin/ssh_login_alert.sh
- Optionally remove script:
sudo rm -f /usr/local/bin/ssh_login_alert.sh
- New SSH sessions will no longer trigger alerts (no restart required for PAM changes).